<template>
  <div
    class="vk-select"
    :class="{ 'is-disabled': disabled }"
    @click="toggleDropdown"
    @mouseenter="states.mouseHover = true"
    @mouseleave="states.mouseHover = false"
  >
    <Tooltip
      placement="bottom-start"
      :manual="true"
      ref="tooltipRef"
      :popper-options="popperOptions"
      @click-outside="controlDropdown(false)"
    >
      <Input
        type="text"
        v-model="states.inputValue"
        :disabled="disabled"
        :placeholder="filteredPlaceHolder"
        :readonly="!filterable || !isDropdownShow"
        @input="onFilter"
        ref="inputRef"
      >
        <template #suffix>
          <Icon
            v-if="showClearIcon"
            icon="circle-xmark"
            class="vk-input__clear"
            @click.stop="onClear"
            @mousedown.prevent="NOOP"
          ></Icon>
          <Icon
            v-else
            icon="angle-down"
            class="header-angle"
            :class="{ 'is-active': isDropdownShow }"
          ></Icon>
        </template>
      </Input>
      <template #content>
        <ul class="vk-select__menu">
          <template v-for="(item, index) in filteredOptions" :key="index">
            <li
              class="vk-select__menu-item"
              :class="{
                'is-disabled': item.disabled,
                'is-selected': states.selectedOption?.value === item.value,
              }"
              :id="`select-item-${item.value}`"
              @click.stop="itemSelect(item)"
            >
              <RenderVnode
                :vNode="renderLabel ? renderLabel(item) : item.label"
              ></RenderVnode>
            </li>
          </template>
        </ul>
      </template>
    </Tooltip>
  </div>
</template>

<script setup lang="ts">
import {
  ref,
  computed,
  defineProps,
  defineEmits,
  defineOptions,
  withDefaults,
  Ref,
  reactive,
  watch,
} from "vue";
import {
  SelectEmits,
  SelectProps,
  SelectOption,
  SelectStates,
  RenderLabelFunc,
} from "./types";
import Tooltip from "../Tooltip/Tooltip.vue";
import Input from "../Input/Input.vue";
import { TooltipInstance } from "../Tooltip/types";
import { InputInstance } from "../Input/types";
import Icon from "../Icon/Icon.vue";
import RenderVnode from "../Common/RenderVnode";
import { isFunction } from "lodash-es";

defineOptions({ name: "VkSelect" });

const findOption = (value: string) => {
  const option = props.options.find((item) => item.value === value);
  return option ? option : null;
};
const props = withDefaults(defineProps<SelectProps>(), {
  modelValue: "",
  placeholder: "请选择",
  remoteMethod: () => Promise.resolve([]), // 远程搜索
  renderLabel: (option: any) => option.label, // 自定义菜单
});

const emits = defineEmits<SelectEmits>();
const tooltipRef = ref() as Ref<TooltipInstance>;
const inputRef = ref() as Ref<InputInstance>;
const isDropdownShow = ref(false);
const initialOption = findOption(props.modelValue);
const states = reactive<SelectStates>({
  inputValue: initialOption ? initialOption.label : "",
  selectedOption: initialOption,
  mouseHover: false,
});
const popperOptions: any = {
  modifiers: [
    {
      name: "offset",
      options: {
        offset: [0, 9],
      },
    },
    {
      name: "sameWidth", // 设置tooltip的宽度和输入框一致
      enabled: true,
      phase: "beforeWrite",
      fn({ state }: { state: any }) {
        state.styles.popper.width = `${state.rects.reference.width}px`;
      },
      requires: ["computeStyles"],
    },
  ],
};

const filteredOptions = ref(props.options);
watch(
  () => props.options,
  (newOptions) => {
    filteredOptions.value = newOptions;
  }
);

const showClearIcon = computed(() => {
  return (
    props.clearable &&
    states.selectedOption &&
    states.inputValue.trim() !== "" &&
    states.mouseHover
  );
});
const onFilter = () => {
  generateFilterOptions(states.inputValue);
};
const generateFilterOptions = (searchValue: string) => {
  if (!props.filterable) return;
  if (props.filterMethod && isFunction(props.filterMethod)) {
    filteredOptions.value = props.filterMethod(searchValue);
  } else {
    // 添加默认的
    filteredOptions.value = props.options.filter((item) =>
      item.label.includes(searchValue)
    );
  }
};

const onClear = () => {
  states.inputValue = "";
  states.selectedOption = null;
  emits("change", "");
  emits("update:modelValue", "");
};

const filteredPlaceHolder = computed(() => {
  return props.filterable && states.selectedOption && isDropdownShow.value
    ? states.selectedOption.label
    : props.placeholder;
});

const controlDropdown = (show: boolean) => {
  if (show) {
    // filterable模式，且之前选择过对应的值
    if (props.filterable && states.selectedOption) {
      states.inputValue = "";
    }
    // 进行一次默认选项的生成
    if (props.filterable) {
      generateFilterOptions(states.inputValue);
    }
    tooltipRef.value?.show();
  } else {
    tooltipRef.value?.hide();
    // blur 的时候，将之前的值赋给input
    if (props.filterable) {
      states.inputValue = states.selectedOption
        ? states.selectedOption.label
        : "";
    }
  }
  isDropdownShow.value = show;
  emits("visible-change", show);
};

const toggleDropdown = () => {
  if (props.disabled) return;
  if (isDropdownShow.value) {
    controlDropdown(false);
  } else {
    controlDropdown(true);
  }
};

const itemSelect = (e: SelectOption) => {
  if (props.disabled) return;
  states.inputValue = e.label;
  states.selectedOption = e;
  emits("change", e.value);
  emits("update:modelValue", e.value);
  controlDropdown(false);
  inputRef.value.ref.focus();
};

// 只是一个空函数,主要解决点击清空图标时，触发了blur事件的问题
const NOOP = () => {};
</script>

<style scoped></style>
